# GTK imports
import pygtk
pygtk.require("2.0")
import gtk
import gobject
import os.path

import serial

from time import gmtime, strftime

# Numpy module
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib.figure import Figure

# Com scanning module need testing
from comscan import comscan

# Initialize GTK threads
gobject.threads_init()

class gui():
    def __init__(self):
        builder = gtk.Builder()
        builder.add_from_file("gui.glade")
        self.w = builder.get_object("mainwindow")
         # Get GTK objects from glade file

        # Reading in all the parameters and creating object to each from the object file
        obs = builder.get_objects()
        for ob in obs:
            try:
                ob_name = gtk.Buildable.get_name(ob)
                exe_string = "self." + ob_name + " = builder.get_object(\"" + ob_name + "\")"
                exec(exe_string)
            except:
                pass

        #cb = gtk.combo_box_new_text()
        #self.combox.append_text("hej")
        self.combobox = gtk.combo_box_new_text()
        self.comhbox.pack_end(self.combobox, expand=True, fill=True, padding=0)
        self.combobox.append_text('Select COM port:')
        self.combobox.set_active(0)
        self.set_and_get_com_ports()
        self.combobox.connect('changed', self.combox_changed_cb)

        self.start_time = None
        self.stop_time = None
        self.sampling_started = False

        # Initializing measurement data
        self.meas = {}
        self.meas['accx'] = []
        self.meas['accy'] = []
        self.meas['accz'] = []
        self.meas['pres'] = []
        self.meas['temp'] = []
        self.meas['height'] = []
        self.meas['height_raw'] = []
        self.meas['time'] = []
        self.current_time = 0
        self.active_plot = 'accx'
        self.ymin = {}
        self.ymin['accx'] = -2
        self.ymin['accy'] = -2
        self.ymin['accz'] = -2
        self.ymin['pres'] = 900
        self.ymin['temp'] = -5
        self.ymin['height'] = -100
        self.ymax = {}
        self.ymax['accx'] = 2
        self.ymax['accy'] = 2
        self.ymax['accz'] = 2
        self.ymax['pres'] = 1200
        self.ymax['temp'] = 35
        self.ymax['height'] = 200
        self.meas_type = {}
        self.meas_type['accx'] = "[g]"
        self.meas_type['accy'] = "[g]"
        self.meas_type['accz'] = "[g]"
        self.meas_type['pres'] = "[kPa]"
        self.meas_type['temp'] = "[C]"
        self.meas_type['height'] = "[m]"

        self.offset = 0

        # Magic ongo bongo to find the correct color for the graphs still it is not quite right but better than default:)
        self.graph_color_box = builder.get_object("graphbox")
        self.graph_color_box.realize()
        col = self.graph_color_box.get_style().bg[gtk.STATE_PRELIGHT]
        col = col.to_string()
        col = col[0:3]+col[5:7]+col[9:11]
        self.graph_color = col

        # Construct plot area
        fig = Figure(facecolor=self.graph_color, edgecolor=self.graph_color)
        self.canvas_plot = FigureCanvas(fig)
        self.graphbox.pack_start(self.canvas_plot, True, True)
        self.plot = fig.add_subplot(111)
        self.init_plot()
        self.canvas_plot.show()
        self.canvas_plot.set_size_request(700, 350)

        # Connecting all signals and start main window
        builder.connect_signals(self)
        self.w.set_title("AAU Universitarium CANSAT")
        self.w.show_all()

        # Start the GTK main thread
        gtk.main()

    def init_plot(self, meastype='accx'):
        self.plot_type(meastype)

    def plot_type(self, meastype):
        self.plot.clear()
        self.plot.plot(self.meas['time'], self.meas[meastype], 'r-o')
        ymin = self.ymin[meastype]
        ymax = self.ymax[meastype]
        self.plot.set_ylim(ymin, ymax)
        self.plot.set_ylabel(self.meas_type[meastype])
        self.plot.yaxis.set_label_coords(-0.07, 0.5)
        self.canvas_plot.draw_idle()

    def update_plot(self):
        self.plot_type(self.active_plot)

    def on_plot_pressed(self, widget):
        print "change plot"
        name = gtk.Buildable.get_name(widget)
        to_plot = name[4:]
        print to_plot
        self.active_plot = to_plot
        self.plot_type(to_plot)
        return

    def height_offset_value_changed_cb(self, widget):
        self.offset = widget.get_value_as_int()
        if self.meas["height"]:
            for i, meas in enumerate(self.meas["height_raw"]):
                self.meas["height"][i] = meas + self.offset
        return

    def set_and_get_com_ports(self):
        self.serials = comscan()
        print "Found COM ports",
        for seria in self.serials:
            name = seria['name']
            print name,
            self.combobox.append_text(name)
            self.combobox.set_active(1)

        print '\n'


    def combox_changed_cb(self, button):
        print "Selected COM port:",
        print button.get_active_text()

    def start_clicked_cb(self, button):
        if self.sampling_started:
            self.error_dialog("Stop sampling", "before starting new")
            return
        if self.combobox.get_active():
            self.sampling_started = True
            print "Starting sampling from",
            name = self.combobox.get_active_text()
            print name
            self.meas['accx'] = []
            self.meas['accy'] = []
            self.meas['accz'] = []
            self.meas['pres'] = []
            self.meas['temp'] = []
            self.meas['height'] = []
            self.meas['height_raw'] = []
            self.meas['time'] = []
            self.current_time = 0
            self.start_time = strftime("%a, %d %b %Y %H:%M:%S", gmtime())
            #setup serial port
            ser = serial.Serial(name, 9600, timeout=0.1,
                    bytesize=serial.EIGHTBITS,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_ONE,
                    xonxoff=0,
                    interCharTimeout=None)
            self.ser_thread = serial_thread(self, ser)
        else:
            self.error_dialog("Select or connect comport", "before starting")
        return

    def stop_clicked_cb(self, button):
        if self.sampling_started:
            self.stop_time = strftime("%a, %d %b %Y %H:%M:%S", gmtime())
            print "Stop sampling"
            print self.start_time
            print self.stop_time
            self.sampling_started = False
            self.ser_thread.stop()
        elif button:
            self.error_dialog("Start sampling", "before stopping")
        return


    def save_clicked_cb(self, button):
        fil = self.save_chooser.get_uri()
        if self.start_time == None:
            self.error_dialog("Start sampling", "before saving")
            return
        elif self.stop_time == None:
            self.error_dialog("Stop sampling", "before saving")
            return
        if fil:
            fil_path = fil[7:]
            if fil_path[-4:] == ".csv":
                pass
            else:
                fil_path += ".csv"
            if os.path.isfile(fil_path):
                self.error_dialog("File already exists", "Only allowed to create new files")
            else:
                try:
                    f = open(fil_path, 'w')
                    f.write("This file consists of measurements from a small gui;;;\n")
                    f.write("start time;" + self.start_time + ";")
                    f.write("stop time;" + self.stop_time)
                    f.write("\n")
                    f.write("sample;accx;accy;accz;pres;temp;height")
                    f.write("\n")
                    for i, sample in enumerate(self.meas['time']):
                        f.write(str(sample))
                        f.write(';')
                        f.write(str(self.meas['accx'][i]))
                        f.write(';')
                        f.write(str(self.meas['accy'][i]))
                        f.write(';')
                        f.write(str(self.meas['accz'][i]))
                        f.write(';')
                        f.write(str(self.meas['pres'][i]))
                        f.write(';')
                        f.write(str(self.meas['temp'][i]))
                        f.write(';')
                        f.write(str(self.meas['height'][i]))
                        f.write("\n")
                    f.close()
                    print "Saving the csv file:",
                    print fil_path
                except IOError as e:
                    self.error_dialog("I/O error({0}): {1}".format(fil_path, e.strerror))
        else:
           self.error_dialog("Select a file", "before saving")
        return

    def error_dialog(self, text_pri, text_sec=None):
        dialog = gtk.MessageDialog(self.w, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, text_pri)
        dialog.format_secondary_text(text_sec)
        dialog.run()
        dialog.destroy()

    # Quit buttons
    def mainwindow_destroy_cb(self, event):
        self.quit()

    def quit_activate_cb(self, event):
        self.quit()

    def quit(self):
        self.stop_clicked_cb(None)
        gtk.main_quit()

import threading
class serial_thread(threading.Thread):
    def __init__(self, gui, ser):
        self.gui = gui
        self.ser = ser
        threading.Thread.__init__(self, None)
        self.running = True
        self.buffer = ''
        self.start()

    def stop(self):
        #self.ser.close()
        self.running = False

    def run(self):
        while self.running:
            self.buffer += self.ser.read(self.ser.inWaiting())
            if '\n' in self.buffer:
                last_received, self.buffer = self.buffer.split('\n')[-2:]
                meas = last_received.split()
                if len(meas) < 6:
                    continue
                height = int(meas[5])
                height_offset = height + self.gui.offset
                self.gui.acc1.set_text(meas[0])
                self.gui.acc2.set_text(meas[1])
                self.gui.acc3.set_text(meas[2])
                self.gui.pres.set_text(meas[3])
                self.gui.temp.set_text(meas[4])
                self.gui.height.set_text(str(height_offset))
                self.gui.meas['accx'].append(meas[0])
                self.gui.meas['accy'].append(meas[1])
                self.gui.meas['accz'].append(meas[2])
                self.gui.meas['pres'].append(meas[3])
                self.gui.meas['temp'].append(meas[4])
                self.gui.meas['height'].append(height_offset)
                self.gui.meas['height_raw'].append(height)
                self.gui.current_time += 1
                self.gui.meas['time'].append(self.gui.current_time)
                gobject.idle_add(self.gui.update_plot)
            pass

if __name__ == "__main__":
    # Start the client
    gui()

